 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import java.lang.reflect.InvocationTargetException ;
 import java.lang.reflect.Method ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtension;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.util.SafeRunnable;
 import org.eclipse.ui.IStartup;
 import org.osgi.framework.Bundle;

 /**
  * A utility class used to call #earlyStartup on the proper instance for a given
  * configuration element. There are a few steps to the process in order to
  * ensure compatibility with pre-3.0 plugins.
  *
  * @since 3.0
  */
 public class EarlyStartupRunnable extends SafeRunnable {

     private static final String EXTENSION_CLASS = "org.eclipse.core.runtime.IExtension"; //$NON-NLS-1$

 // private static final String PLUGIN_DESC_CLASS = "org.eclipse.core.runtime.IPluginDescriptor"; //$NON-NLS-1$

     private static final String GET_PLUGIN_METHOD = "getPlugin"; //$NON-NLS-1$

     private static final String GET_DESC_METHOD = "getDeclaringPluginDescriptor"; //$NON-NLS-1$

     private static final String PI_RUNTIME_COMPATIBILITY = "org.eclipse.core.runtime.compatibility"; //$NON-NLS-1$

     private IExtension extension;

     /**
      * @param extension
      * must not be null
      */
     public EarlyStartupRunnable(IExtension extension) {
         this.extension = extension;
     }

     public void run() throws Exception {
         IConfigurationElement[] configElements = extension
                 .getConfigurationElements();

         // look for the startup tag in each element and run the extension
 boolean foundAtLeastOne = false;
         for (int i = 0; i < configElements.length; ++i) {
             IConfigurationElement element = configElements[i];
             if (element != null
                     && element.getName()
                             .equals(IWorkbenchConstants.TAG_STARTUP)) {
                 runEarlyStartup(getExecutableExtension(element));
                 foundAtLeastOne = true;
             }
         }

         // if no startup tags were found, then try the plugin object
 if (!foundAtLeastOne) {
             runEarlyStartup(getPluginForCompatibility());
         }
     }

     public void handleException(Throwable exception) {
         IStatus status = new Status(IStatus.ERROR, extension.getNamespace(), 0,
                 "Unable to execute early startup code for an extension", //$NON-NLS-1$
 exception);
         WorkbenchPlugin.log("Unhandled Exception", status); //$NON-NLS-1$
 }

     private void runEarlyStartup(Object executableExtension) {
         if (executableExtension != null
                 && executableExtension instanceof IStartup) {
             ((IStartup) executableExtension).earlyStartup();
         } else {
             IStatus status = new Status(IStatus.ERROR,
                     extension.getNamespace(), 0,
                     "startup class must implement org.eclipse.ui.IStartup", //$NON-NLS-1$
 null);
             WorkbenchPlugin.log("Bad extension specification", status); //$NON-NLS-1$
 }
     }

     /**
      * In 3.0 the class attribute is a mandatory element of the startup element.
      * However, 2.1 plugins should still be able to run if the compatibility
      * bundle is loaded.
      *
      * @return an executable extension for this startup element or null if an
      * extension (or plugin) could not be found
      */
     private Object getExecutableExtension(IConfigurationElement element)
             throws CoreException {

         String classname = element.getAttribute(IWorkbenchConstants.TAG_CLASS);

         // if class attribute is absent then try to use the compatibility
 // bundle to return the plugin object
 if (classname == null || classname.length() <= 0) {
             return getPluginForCompatibility();
         }

         // otherwise the 3.0 runtime should be able to do it
 return WorkbenchPlugin.createExtension(element,
                 IWorkbenchConstants.TAG_CLASS);
     }

     /**
      * If the compatiblity bundle is loaded, then return the plugin object for
      * the extension on this runnable. Return null if the compatibility bundle
      * is not loaded or the plugin object cannot be created.
      */
     private Object getPluginForCompatibility() {
         // make sure the compatibility bundle is available
 Bundle compatBundle = Platform.getBundle(PI_RUNTIME_COMPATIBILITY);
         if (compatBundle == null) {
             return null;
         }

         // use reflection to try to access the plugin object
 try {
             // IPluginDescriptor pluginDesc =
 // extension.getDeclaringPluginDescriptor();
 Class extensionClass = compatBundle.loadClass(EXTENSION_CLASS);
             Method getDescMethod = extensionClass.getDeclaredMethod(
                     GET_DESC_METHOD, new Class [0]);
             Object pluginDesc = getDescMethod.invoke(extension, new Object [0]);
             if (pluginDesc == null) {
                 return null;
             }

             // Plugin plugin = pluginDesc.getPlugin();
 Class pluginDescClass = pluginDesc.getClass();
             Method getPluginMethod = pluginDescClass.getDeclaredMethod(
                     GET_PLUGIN_METHOD, new Class [0]);
             return getPluginMethod.invoke(pluginDesc, new Object [0]);
         } catch (ClassNotFoundException e) {
             handleException(e);
         } catch (IllegalAccessException e) {
             handleException(e);
         } catch (InvocationTargetException e) {
             handleException(e);
         } catch (NoSuchMethodException e) {
             handleException(e);
         }

         return null;
     }
 }

